package com.stars.easyms.schedule.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 反射工具类
 *
 * @author guoguifang
 */
public class ReflectUtil {

    private static final Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
    private static final String ARRAY_SUFFIX = "[]";
    private static final String INTERNAL_ARRAY_PREFIX = "[";
    private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L";
    private static final char PACKAGE_SEPARATOR = '.';
    private static final char INNER_CLASS_SEPARATOR = '$';

    /**
     * 获取默认的类加载器
     */
    public static ClassLoader getDefaultClassLoader() {
        ClassLoader classLoader = null;
        try {
            classLoader = Thread.currentThread().getContextClassLoader();
        } catch (Throwable t) {
            if (logger.isDebugEnabled()) {
                logger.debug("Get current thread classLoader failure!", t);
            }
        }
        if (classLoader == null) {
            classLoader = ReflectUtil.class.getClassLoader();
            if (classLoader == null) {
                try {
                    classLoader = ClassLoader.getSystemClassLoader();
                } catch (Throwable t) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Get system classLoader failure!", t);
                    }
                }
            }
        }
        return classLoader;
    }

    /**
     * 根据类名获取类
     */
    public static Class<?> forName(String className) throws ClassNotFoundException {
        if (className == null) {
            return null;
        }
        if (className.endsWith(ARRAY_SUFFIX)) {
            String elementClassName = className.substring(0, className.length() - ARRAY_SUFFIX.length());
            Class<?> elementClass = forName(elementClassName);
            return Array.newInstance(elementClass, 0).getClass();
        }
        if (className.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && className.endsWith(";")) {
            String elementName = className.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), className.length() - 1);
            Class<?> elementClass = forName(elementName);
            return Array.newInstance(elementClass, 0).getClass();
        }
        if (className.startsWith(INTERNAL_ARRAY_PREFIX)) {
            String elementName = className.substring(INTERNAL_ARRAY_PREFIX.length());
            Class<?> elementClass = forName(elementName);
            return Array.newInstance(elementClass, 0).getClass();
        }
        try {
            return Class.forName(className, false, getDefaultClassLoader());
        } catch (ClassNotFoundException ex) {
            int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
            if (lastDotIndex != -1) {
                String innerClassName = className.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + className.substring(lastDotIndex + 1);
                try {
                    return Class.forName(innerClassName, false, getDefaultClassLoader());
                } catch (ClassNotFoundException ex1) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Get inner class '" + innerClassName + "' failure!", ex1);
                    }
                }
            }
            throw ex;
        }
    }

    /**
     * 根据类名获取类,不抛出异常
     */
    public static Class<?> forNameWithNoException(String className) {
        try {
            return forName(className);
        } catch (Throwable t) {
            if (logger.isDebugEnabled()) {
                logger.debug("Get Class failure based on class name '" + className + "'!", t);
            }
        }
        return null;
    }

    /**
     * 根据方法名获取方法
     */
    public static Method getMethodByName(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        if (clazz != null && methodName != null) {
            Method method = null;
            try {
                method = clazz.getMethod(methodName, parameterTypes);
            } catch (Exception e) {
                try {
                    method = clazz.getDeclaredMethod(methodName, parameterTypes);
                } catch (Exception e1) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Get Method failure based on method name '" + methodName + "' of class '" + clazz.getName() + "'!", e1);
                    }
                }
            }
            if (method != null) {
                method.setAccessible(true);
            }
            return method;
        }
        return null;
    }

    /**
     * 根据属性名获取属性
     */
    public static Object getFieldByName(Class<?> clazz, String fieldName, Object obj) {
        if (clazz != null && fieldName != null && obj != null) {
            Field field = null;
            try {
                field = clazz.getField(fieldName);
            } catch (Exception e) {
                try {
                    field = clazz.getDeclaredField(fieldName);
                } catch (Exception e1) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Get Field failure based on field name '" + fieldName + "' of class '" + clazz.getName() + "'!", e1);
                    }
                }
            }
            if (field != null) {
                try {
                    field.setAccessible(true);
                    return field.get(obj);
                } catch (IllegalAccessException ignore) {
                    return null;
                }
            }
            return null;
        }
        return null;
    }

    /**
     * 获取构造方法
     */
    public static Constructor getConstructor(Class<?> clazz, Class<?>... parameterTypes) {
        if (clazz != null && parameterTypes != null) {
            try {
                return clazz.getConstructor(parameterTypes);
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Get Constructor failure based on class '" + clazz.getName() + "'!", e);
                }
            }
        }
        return null;
    }

    /**
     * 获取类的一个实例
     */
    public static Object newInstance(Class clazz) {
        if (clazz != null) {
            try {
                return clazz.newInstance();
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Instance class '" + clazz.getName() + "'object failed!", e);
                }
            }
        }
        return null;
    }

    /**
     * 获取类的一个实例
     */
    public static Object newInstance(Constructor constructor, Object... initArgs) {
        if (constructor != null && initArgs != null) {
            try {
                return constructor.newInstance(initArgs);
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Instance class '" + constructor.getDeclaringClass().getName() + "'object failed!", e);
                }
            }
        }
        return null;
    }

    /**
     * 执行方法
     */
    public static Object invoke(Method method, Object bean, Object... args) {
        if (method != null && bean != null && args != null) {
            try {
                return method.invoke(bean, args);
            } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Invoke method '" + method.getName() + "' of class '" + method.getDeclaringClass().getName() + "'failed!", e);
                }
            }
        }
        return null;
    }

}
